package com.flexdms.jsfutils;

import java.io.IOException;

import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.view.facelets.AttributeHandler;
import javax.faces.view.facelets.ComponentHandler;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.TagAttribute;
import javax.faces.view.facelets.TagConfig;
import javax.faces.view.facelets.TagException;
import javax.faces.view.facelets.TagHandler;

/**
 *  A tag handler like f:attribute, but it evaluates the value expression immediately and 
 *  set the result as attribute value. f:attribute sets the value expression to component 
 *  ValueExpression map. Mainly used inside of c:forEach.
 *  
 * @author jason
 */
public class BuildAttributeHandler extends TagHandler implements
		AttributeHandler
{

	private final TagAttribute name;

	private final TagAttribute value;
	private final TagAttribute ignoreNull;
	private final TagAttribute skipIfExist;
	private final TagAttribute passThrough;
	private final TagAttribute valueType;
	protected final TagAttribute donotEval;
	

	public BuildAttributeHandler(TagConfig config)
	{
		super(config);
		this.name = this.getRequiredAttribute("name");
		this.value = this.getRequiredAttribute("value");
		this.ignoreNull=this.getAttribute("ignoreNull");
		this.skipIfExist=this.getAttribute("skipIfExist");
		this.passThrough=this.getAttribute("passThrough");
		this.valueType=this.getAttribute("valueType");
		this.donotEval=this.getAttribute("donotEval");
	}

	@Override
	public void apply(FaceletContext ctx, UIComponent parent)
			throws IOException
	{
		if (parent == null)
		{
			throw new TagException(this.tag, "Parent UIComponent was null");
		}

		// only process if the parent is new to the tree
		if (ComponentHandler.isNew(parent)) 
		{
			String n = getAttributeName(ctx);
			if (!parent.getAttributes().containsKey(n))
			{
				if (this.donotEval!=null && this.donotEval.getBoolean(ctx))
				{
					//treat value as literal
					parent.getAttributes().put(n, this.value.getValue());
				} else if (this.value.isLiteral())
				{
					String strValue=this.value.getValue();
					Object v=strValue;
					if (this.valueType!=null && this.valueType.getValue()!=null)
					{
						try
						{
							v=ctx.convertToType(strValue, Class.forName(this.valueType.getValue()));
						} catch (ClassNotFoundException e)
						{
							throw new RuntimeException(e);
						}
					}
					parent.getAttributes().put(n, v);
				} else
				{
					Object value=this.value.getValueExpression(ctx, Object.class).getValue(FacesContext.getCurrentInstance().getELContext());
					boolean ignoreNull=true;
					if (this.ignoreNull!=null )
					{
						ignoreNull=this.ignoreNull.getBoolean(ctx);
					}
					boolean skipIfExist=true;
					if (this.skipIfExist!=null )
					{
						skipIfExist=this.skipIfExist.getBoolean(ctx);
					}
					
					boolean passThrough=false;
					if (this.passThrough!=null)
					{
						passThrough=this.passThrough.getBoolean(ctx);
					}
					if (value==null && ignoreNull)
					{
						return;
					}
				
					if (!passThrough)
					{
						if (skipIfExist && parent.getAttributes().containsKey(n))
						{
							return;
						}
						if (value instanceof String)
						{
								ValueExpression ve=ctx.getExpressionFactory().createValueExpression(ctx, value.toString(), Object.class);
								if (!ve.isLiteralText())
								{
									parent.setValueExpression(n, ve);
									return;
								} else
								{
									if (this.valueType!=null && this.valueType.getValue()!=null)
									{
										try
										{
											value=ctx.convertToType(value, Class.forName(this.valueType.getValue()));
										} catch (ClassNotFoundException e)
										{
											throw new RuntimeException(e);
										}
									}
								}
						} else
						{
							if (this.valueType!=null && this.valueType.getValue()!=null)
							{
								try
								{
									value=ctx.convertToType(value, Class.forName(this.valueType.getValue()));
								} catch (ClassNotFoundException e)
								{
									throw new RuntimeException(e);
								}
							}
						}
						parent.getAttributes().put(n, value);
					} else
					{
						if (skipIfExist && parent.getPassThroughAttributes()!=null && parent.getPassThroughAttributes().containsKey(n))
						{
							return;
						}
						if (value instanceof String)
						{
							ValueExpression ve=ctx.getExpressionFactory().createValueExpression(ctx, value.toString(), Object.class);
							if (!ve.isLiteralText())
							{
								value=ve;
							} else
							{
								if (this.valueType!=null && this.valueType.getValue()!=null)
								{
									try
									{
										value=ctx.convertToType(value, Class.forName(this.valueType.getValue()));
									} catch (ClassNotFoundException e)
									{
										throw new RuntimeException(e);
									}
								}
							}
						} else
						{
							if (this.valueType!=null && this.valueType.getValue()!=null)
							{
								try
								{
									value=ctx.convertToType(value, Class.forName(this.valueType.getValue()));
								} catch (ClassNotFoundException e)
								{
									throw new RuntimeException(e);
								}
							}
						}
						parent.getPassThroughAttributes(true).put(n, value);
					}
				}
			}
		}

	}

	@Override
	public String getAttributeName(FaceletContext ctx)
	{
		return this.name.getValue(ctx);
	}

}
